package spring.security.browser.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.social.security.SpringSocialConfigurer;

import spring.security.browser.logout.DemoLogoutSuccessHandler;
import spring.security.browser.session.DemoExpiredSessionStrategy;
import spring.security.core.mobile.SmsCodeAuthenticationSecurityConfig;
import spring.security.core.mobile.filter.SmsCodeFilter;
import spring.security.core.properties.SecurityProperties;
import spring.security.core.validatecode.ValidateCodeFilter;

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{

	@Autowired
	private SecurityProperties securityProperties;
	
	@Autowired
	private AuthenticationSuccessHandler loginAuthenticationSuccessHandler;
	
	@Autowired
	private AuthenticationFailureHandler loginAuthenticationFailureHandler;
	
	@Autowired
	private UserDetailsService userDetailsService;
	
	@Autowired
	private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
	
	@Autowired
	private SpringSocialConfigurer demoSpringSocialConfig;
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Autowired
	private DataSource dataSource;
	
	@Bean
	public PersistentTokenRepository persistentTokenRepository() {
		JdbcTokenRepositoryImpl  repository = new JdbcTokenRepositoryImpl();
		repository.setDataSource(dataSource);
		//repository.setCreateTableOnStartup(true);//启动时需要建表
		return repository;
	}
	
	@Bean
	@ConditionalOnMissingBean(LogoutSuccessHandler.class)
	public LogoutSuccessHandler demoLogoutSuccessHandler() {
		return new DemoLogoutSuccessHandler();
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
		validateCodeFilter.setAuthenticationFailureHandler(loginAuthenticationFailureHandler);
		validateCodeFilter.setSecurityProperties(securityProperties);
		validateCodeFilter.afterPropertiesSet();
		
		SmsCodeFilter smsCodeFilter = new SmsCodeFilter();
		smsCodeFilter.setAuthenticationFailureHandler(loginAuthenticationFailureHandler);
		smsCodeFilter.setSecurityProperties(securityProperties);
		smsCodeFilter.afterPropertiesSet();
		
		//http.httpBasic()//原始登录
		http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
			.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class)
			.formLogin()//表单登录
			.loginPage("/auto/require")
			.loginProcessingUrl("/auth/form")
			.successHandler(loginAuthenticationSuccessHandler)
			.failureHandler(loginAuthenticationFailureHandler)
			//记住我功能
			.and()
			.rememberMe()
				.tokenRepository(persistentTokenRepository())
				.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
				.userDetailsService(userDetailsService)
				.and()
			//拦截路径配置
			.sessionManagement()
				.invalidSessionUrl("/session/invalid")
				.maximumSessions(1)//后面的一个用户踢下前一个用户
				.maxSessionsPreventsLogin(true)//当Session达到最大数量,就会阻止后面用户的登录
				.expiredSessionStrategy(new DemoExpiredSessionStrategy())//并发登录的异常处理
				.and()
				.and()
			.logout()
				.logoutUrl("/signUrl")//登出之后访问的路径
				.logoutSuccessUrl("/logout.html")//登出成功访问的路径
				//.logoutSuccessHandler(logoutSuccessHandler)
				.and()
			.authorizeRequests()//请求授权
			.antMatchers("/auto/require",
					securityProperties.getBrowser().getLoginPage(),
					securityProperties.getBrowser().getSignUpPage(),
					"/code/*", 
					"/user/regist", 
					"/session/invalid").permitAll()
			.anyRequest()//任何请求
			.authenticated()
			.and().csrf().disable()//都需要身份认证
			.apply(smsCodeAuthenticationSecurityConfig)
			.and()
			.apply(demoSpringSocialConfig);
	}
	
}
